﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;

namespace Common.Infrastructure
{
    public static class TypeExtensions
    {
        /// <summary>
        /// 判断类型是否为Nullable类型.
        /// </summary>
        /// <param name="type"> 要处理的类型. </param>
        /// <returns> 是返回True，不是返回False. </returns>
        public static bool IsNullableType(this Type type) => type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);

        /// <summary>
        /// 由类型的Nullable类型返回实际类型.
        /// </summary>
        /// <param name="type"> 要处理的类型对象.</param>
        /// <returns>实际类型.</returns>
        public static Type GetNonNummableType(this Type type) => type.IsNullableType() ? type.GetGenericArguments()[0] : type;

        /// <summary>
        /// 通过类型转换器获取Nullable类型的基础类型.
        /// </summary>
        /// <param name="type"> 要处理的类型对象.</param>
        /// <returns>实际类型对象.</returns>
        public static Type GetUnNullableType(this Type type)
        {
            Type result;
            if (!type.IsNullableType())
            {
                result = type;
            }
            else
            {
                NullableConverter nullableConverter = new NullableConverter(type);
                result = nullableConverter.UnderlyingType;
            }

            return result;
        }

        /// <summary>
        /// 检查指定指定类型成员中是否存在指定的Attribute特性.
        /// </summary>
        /// <typeparam name="T">要检查的Attribute特性类型.</typeparam>
        /// <param name="memberInfo">要检查的类型成员.</param>
        /// <param name="inherit">是否从继承中查找.</param>
        /// <returns>是否存在.</returns>
        public static bool ExistsAttribute<T>(this MemberInfo memberInfo, bool inherit = false)
            where T : Attribute
            => memberInfo.GetCustomAttributes(typeof(T), inherit).Any((object m) => m is T);

        /// <summary>
        /// 从类型成员获取指定Attribute特性.
        /// </summary>
        /// <typeparam name="T">Attribute特性类型.</typeparam>
        /// <param name="memberInfo">类型类型成员.</param>
        /// <param name="inherit">是否从继承中查找.</param>
        /// <returns>存在返回第一个，不存在返回null.</returns>
        public static T GetAttribute<T>(this MemberInfo memberInfo, bool inherit = false)
            where T : Attribute
        {
            object[] customAttributes = memberInfo.GetCustomAttributes(typeof(T), inherit);
            return customAttributes.FirstOrDefault<object>() as T;
        }

        /// <summary>
        /// 从类型成员获取指定Attribute特性.
        /// </summary>
        /// <typeparam name="T">Attribute特性类型.</typeparam>
        /// <param name="memberInfo">类型类型成员.</param>
        /// <param name="inherit">是否从继承中查找.</param>
        /// <returns>返回所有指定Attribute特性的数组.</returns>
        public static T[] GetAttributes<T>(this MemberInfo memberInfo, bool inherit = false)
            where T : Attribute => memberInfo.GetCustomAttributes(typeof(T), inherit).Cast<T>().ToArray<T>();

        /// <summary>
        /// 获取成员元数据的Description特性描述信息.
        /// </summary>
        /// <param name="member">成员元数据对象.</param>
        /// <param name="inherit">是否搜索成员的继承链以查找描述特性.</param>
        /// <returns>返回Description特性描述信息，如不存在则返回成员的名称.</returns>
        public static string ToDescription(this MemberInfo member, bool inherit = false)
        {
            DescriptionAttribute attribute = member.GetAttribute<DescriptionAttribute>(inherit);
            return (attribute == null) ? member.Name : attribute.Description;
        }

        /// <summary>
        /// 判断类型是否为集合类型.
        /// </summary>
        /// <param name="type">要处理的类型.</param>
        /// <returns>是返回True，不是返回False.</returns>
        public static bool IsEnumerable(this Type type) => !(type == typeof(string)) && typeof(System.Collections.IEnumerable).IsAssignableFrom(type);

        /// <summary>
        /// 判断当前泛型类型是否可由指定类型的实例填充.
        /// </summary>
        /// <param name="genericType">泛型类型.</param>
        /// <param name="type">指定类型.</param>
        /// <returns>是否可由指定类型.</returns>
        public static bool IsGenericAssignableFrom(this Type genericType, Type type)
        {
            bool result;
            if (genericType == null || type == null)
            {
                result = false;
            }
            else
            {
                if (!genericType.IsGenericType)
                {
                    throw new ArgumentException("该功能只支持泛型类型的调用，非泛型类型可使用 IsAssignableFrom 方法。");
                }

                List<Type> list = new List<Type>
                {
                    type,
                };
                if (genericType.IsInterface)
                {
                    list.AddRange(type.GetInterfaces());
                }

                foreach (Type current in list)
                {
                    Type type2 = current;
                    while (type2 != null)
                    {
                        if (type2.IsGenericType)
                        {
                            type2 = type2.GetGenericTypeDefinition();
                        }

                        if (type2.IsSubclassOf(genericType) || type2 == genericType)
                        {
                            result = true;
                            return result;
                        }

                        type2 = type2.BaseType;
                    }
                }

                result = false;
            }

            return result;
        }
    }
}
